package com.atguigu.gmall.order.biz.impl;
import com.atguigu.gmall.mq.logistic.OrderLogisticMsg;
import com.google.common.collect.Lists;

import java.util.Date;
        import java.math.BigDecimal;
        import java.util.List;
        import java.util.UUID;
        import java.util.concurrent.Executors;
        import java.util.concurrent.ScheduledExecutorService;
        import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.gmall.cart.entity.CartInfo;
        import com.atguigu.gmall.common.config.rabbitMq.MqService;
        import com.atguigu.gmall.common.constant.MqConst;
        import com.atguigu.gmall.common.constant.RedisConst;
        import com.atguigu.gmall.common.execption.GmallException;
        import com.atguigu.gmall.common.result.ResultCodeEnum;
        import com.atguigu.gmall.common.util.UserAuthUtil;

        import com.atguigu.gmall.enums.OrderStatus;
        import com.atguigu.gmall.enums.ProcessStatus;
        import com.atguigu.gmall.feign.cart.CartFeignClient;

        import com.atguigu.gmall.feign.product.ProductSkuDataFeign;
        import com.atguigu.gmall.feign.user.UserFeignClient;
        import com.atguigu.gmall.feign.ware.WareFeignClient;
        import com.atguigu.gmall.model.enums.PaymentWay;;
import com.atguigu.gmall.mq.ware.WareStockResult;
import com.atguigu.gmall.order.entity.OrderDetail;
        import com.atguigu.gmall.order.entity.OrderInfo;
        import com.atguigu.gmall.order.service.OrderDetailService;
        import com.atguigu.gmall.order.service.OrderInfoService;
import com.atguigu.gmall.order.vo.OrderSplitRespVo;
import com.atguigu.gmall.order.vo.OrderSubmitVo;
        import com.atguigu.gmall.order.vo.OrderconfirmRespVo;
import com.atguigu.gmall.order.vo.SkuWare;
import com.atguigu.gmall.user.entity.UserAddress;


        import com.atguigu.gmall.order.biz.OrderBizService;
        import lombok.extern.slf4j.Slf4j;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.data.redis.core.StringRedisTemplate;
        import org.springframework.stereotype.Service;
        import org.springframework.transaction.annotation.Transactional;
        import org.springframework.web.bind.annotation.PostMapping;
        import org.springframework.web.bind.annotation.RequestParam;

@Service
@Slf4j
public class OrderBizServiceImpl implements OrderBizService {
    // TODO 远程调用购物车
    @Autowired
    private CartFeignClient  cartFeignClient;//购物车微服务
    @Autowired
    private ProductSkuDataFeign skuDataFeign; //商品微服务
    @Autowired
    private UserFeignClient  userFeignClient; //用户微服务
    @Autowired
    private WareFeignClient  wareFeignClient;  //调用导入库从系统
    @Autowired
    private StringRedisTemplate   redisTemplate;
    @Autowired
    OrderInfoService  orderInfoService;
    @Autowired
    private  OrderDetailService  detailService;
    @Autowired
    private   MqService mqService;
    @Autowired
    private  OrderDetailService  orderDetailService;

    @Override
    public OrderconfirmRespVo getConfirmData() {
        OrderconfirmRespVo  respVo =new OrderconfirmRespVo();
        // TODO  1 :远程调用 购物车  拿到购物车中的信息
        List<CartInfo> data = cartFeignClient.getChecked().getData(); //todo 防止请求头数据丢失
        List<OrderconfirmRespVo.SkuDetail> list = data.stream()
                .map(item -> {
                    OrderconfirmRespVo.SkuDetail detail = new OrderconfirmRespVo.SkuDetail();
                    detail.setSkuId(item.getSkuId());
                    detail.setImgUrl(item.getImgUrl());
                    detail.setSkuName(item.getSkuName());
                    detail.setSkuNum(item.getSkuNum());
                    //TODO 商品的价格会实时变化 所以要商品库在查询一次
                    BigDecimal price = skuDataFeign.getPrice(item.getSkuId()).getData();
                    detail.setOrderPrice(price); //TODO 单价
                    //TODO 调用 WareFeign 远程服务
                    String hasStock = wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum());
                    detail.setHasStock(hasStock);
                    return detail;
                }).collect(Collectors.toList());
        respVo.setDetailArrayList(list);

        //2 TODO 2:都获取总数量
        Integer totalNum = list.stream().map(OrderconfirmRespVo.SkuDetail::getSkuNum)
                .reduce((o1, o2) -> o1 + o2)
                .get();
        respVo.setTotalNum(totalNum);
        // TODO 3:获取总金额
        BigDecimal totalAmount = list.stream()
                .map(item -> item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        respVo.setTotalAmount(totalAmount);
        // TODO 4: 获取收货地址
        //1 要获取userId  要从客户端的请求头里面来拿
        Long userId= UserAuthUtil.getUserId();
        //2 TODO 调用用户服务
        List<UserAddress> addressList = userFeignClient.getUserAddress(userId).getData();
        respVo.setUserAddressList(addressList);

        //TODO 5 获取流水号
        //TODO  开启整个订单的追踪业务
        //TODO 防止订单重复提交
        String tradeNo ="atguigujfn"+System.currentTimeMillis()+"-"+userId;
        //给客户端一个流水号
        respVo.setTradeNo(tradeNo);
        //给 redis放一个流水号
        redisTemplate.opsForValue().set(RedisConst.REPEAT_TOKEN+tradeNo,"1",5,
                TimeUnit.MINUTES);
        return respVo;
    }

    @Override
    @Transactional
    public Long submitOrder(String tradeNo, OrderSubmitVo submitVo) {
        // 超卖
        //正常下单是允许超卖 无需提前锁库存  提前锁会导致恶意占库

        //TODO 问题一  检验
        // TODO  参数校验 业务校验
        //TODO 1 令牌校验
        Boolean aBoolean = redisTemplate.delete(RedisConst.REPEAT_TOKEN + tradeNo);
        if(!aBoolean){
            throw  new GmallException(ResultCodeEnum.REPEAT_REQUEST);
        }
        //TODO 2 校验库存
        List<OrderSubmitVo.OrderDetailListDTO> noStockSku = submitVo.getOrderDetailList().stream()
                .filter(item ->
                        "0".equals(wareFeignClient.hasStock(item.getSkuId(), item.getSkuNum()))
                ).collect(Collectors.toList());
        if(noStockSku!=null&&noStockSku.size()>0){
            String skuNames=  noStockSku.stream()
                    .map(OrderSubmitVo.OrderDetailListDTO::getSkuName)
                    .reduce((o1, o2) -> o1 + ";" + o2)
                    .get();
            //TODO 如果库存中没有
            GmallException exception =
                    new GmallException(skuNames + " 没有库存",ResultCodeEnum.NO_STOCK.getCode());
            throw  exception;
        }
        //TODO 3 校验价格
        List<OrderSubmitVo.OrderDetailListDTO> priceChange = submitVo.getOrderDetailList().stream()
                .filter(item -> {
                    BigDecimal orderPrice = item.getOrderPrice();
                    //TODO 去数据库查最新的价格
                    BigDecimal price = skuDataFeign.getPrice(item.getSkuId()).getData();
                    //TODO 返回价格差值的绝对值
                    return Math.abs(orderPrice.subtract(price).doubleValue()) >= 0.0001;
                }).collect(Collectors.toList());
        if(priceChange!=null&&priceChange.size()>0){
            //TODO 将所有商品名进行一个叠加操作 字符串拼接
            String skuNames = priceChange.stream()
                    .map(OrderSubmitVo.OrderDetailListDTO::getSkuName)
                    .reduce((o1, o2) -> o1 + ";" + o2)
                    .get();
            GmallException exception =
                    new GmallException(skuNames + "； 价格变化，请刷新页面重新确认",
                            ResultCodeEnum.PRICE_CHANGE.getCode());
            throw  exception;

        }
        // TODO 4  给 order_Info 保存订单的基本信息
        OrderInfo orderInfo= prepareOrderInfo(submitVo,tradeNo);
        orderInfoService.save(orderInfo);

        //TODO 5 以雪花算法生成  订单 ID
        Long orderId = orderInfo.getId();
        //order-detail 保存订单明细
       List<OrderDetail> orderDetailList=prepareOrderDetails(submitVo,orderInfo);
       detailService.saveBatch(orderDetailList);
        //3、发送订单创建成功消息
        mqService.send(orderInfo, MqConst.ORDER_EVENT_EXCHANGE, MqConst.ORDER_CREATE_RK);

       //TODO 6 删除购物车所选项
        cartFeignClient.deleteChecked();
        return  orderId;
    }

    /**
     * 关闭订单
     * @param id
     * @param userId
     */
    @Override
    public void closeOrder(Long id, Long userId) {
        ProcessStatus closed = ProcessStatus.CLOSED;

        //只有未支付的情况下 才需要关闭
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, closed.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus, closed.name())
                .eq(OrderInfo::getId, id)
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name())
                .eq(OrderInfo::getProcessStatus, OrderStatus.UNPAID.name())
                .update();
        log.info("订单关闭",id,update);

    }

    @Override
    public void updateOrderStatus(String out_trade_no, long userId) {
     //  TODO 关单消息和支付消息 同时抵达  ，无论谁先进行，最终结果都是已支付
        // 1关单先运行 改为已关闭  支付后运行 应该最后改为已支付
        ProcessStatus payed = ProcessStatus.PAID;
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, payed.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus, payed.name())
                .eq(OrderInfo::getUserId, userId)
                .eq(OrderInfo::getOutTradeNo, out_trade_no)
                .in(OrderInfo::getOrderStatus, OrderStatus.UNPAID.name(), OrderStatus.CLOSED.name())
                .in(OrderInfo::getProcessStatus, OrderStatus.UNPAID.name(), OrderStatus.CLOSED.name())
                .update();
              log.info("修改为已支付状态",out_trade_no,update);
              //1 :保存此支付回调的数据信息 payment_info
             // 2: 支付先运行 改为已支付  后面什么都不做
    }

    @Override
    public void updateOrderStockStatus(WareStockResult result) {
        ProcessStatus status=ProcessStatus.WAITING_DELEVER;
        // TODO  1 最终订单状态
        switch (result.getStatus()){
            case "DEDUCTED": status=ProcessStatus.WAITING_DELEVER; break;
            case "OUT_OF_STOCK":status=ProcessStatus.WAITING_SCHEDULE;break;
        }
        //2 修改订单状态
        //TODO 消息队列与http请求没有任何关系
        OrderInfo info = orderInfoService.getById(result.getOrderId());
        orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus,status.getOrderStatus().name())
                .set(OrderInfo::getProcessStatus,status.name())
                .eq(OrderInfo::getId,info.getId())
                .eq(OrderInfo::getUserId,info.getUserId())
                .eq(OrderInfo::getOrderStatus,OrderStatus.PAID.name())
                .eq(OrderInfo::getProcessStatus,ProcessStatus.PAID.name())
                .update();
        log.info("订单状态修改完成");
        //下电子面单  准备发货
        if("DEDUCTED".equals(result.getStatus())) {
           OrderLogisticMsg msg =new OrderLogisticMsg();
           msg.setOrderId(info.getId());
           msg.setUserId(info.getUserId());

            mqService.send(msg, MqConst.ORDER_EVENT_EXCHANGE, MqConst.ORDER_LOGISTIC_RK);
        }

    }

    /**
     * 拆弹
     * @param orderId
     * @param json
     * @return
     */
    @Override
    public List<OrderSplitRespVo> orderSplit(Long orderId, String json) {
        //TODO 把一个大订单 按照库存分布拆分成子订单 ，将子订单放入数据库中
        OrderInfo parentOrder = orderInfoService.getById(orderId);
       //TODO 拿到大订单的明细表
        List<OrderDetail> orderDetails = orderDetailService.lambdaQuery()
                .eq(OrderDetail::getOrderId, parentOrder.getId())
                .eq(OrderDetail::getUserId, parentOrder.getUserId())
                .list();
        // TODO 1 得到大订单里面所以的库存分布
        List<SkuWare> skuWares = JSON.parseObject(json, new TypeReference<List<SkuWare>>() {
        });
        AtomicInteger i = new AtomicInteger(0);
        //TODO 拆分大订单
        List<OrderInfo> childOrders =skuWares.stream().map(item->{
             OrderInfo childOrder  =new OrderInfo();
             childOrder.setConsignee(parentOrder.getConsignee());
             childOrder.setConsigneeTel(parentOrder.getConsigneeTel());
             //TODO 子订单总额
            List<Long> skuIds = item.getIds(); //当前子订单的所以商品id
            //拿到子订单负责的所以商品
            List<OrderDetail> childDetails = orderDetails.stream()
                    .filter(obj -> skuIds.contains(obj.getSkuId()))
                    .collect(Collectors.toList());
            childOrder.setOrderDetails(childDetails);


            //计算子订单总额
            BigDecimal totalAmount = childDetails.stream().map(o1 ->
                                     o1.getOrderPrice()
                                    .multiply(new BigDecimal(o1.getSkuNum())))
                                    .reduce((o1, o2) -> o1.add(o2))
                                    .get();

             childOrder.setTotalAmount(totalAmount);
             childOrder.setOrderStatus(parentOrder.getOrderStatus());
             childOrder.setUserId(parentOrder.getUserId());
             childOrder.setPaymentWay(parentOrder.getPaymentWay());
             childOrder.setDeliveryAddress(parentOrder.getDeliveryAddress());
             childOrder.setOrderComment(parentOrder.getOrderComment());
             childOrder.setOutTradeNo(parentOrder.getOutTradeNo());
             childOrder.setTradeBody(childDetails.get(0).getSkuName());
             childOrder.setCreateTime(new Date());
             childOrder.setExpireTime(parentOrder.getExpireTime());
             childOrder.setProcessStatus(parentOrder.getProcessStatus());
             childOrder.setTrackingNo("");
             childOrder.setParentOrderId(parentOrder.getId());
             childOrder.setImgUrl(childDetails.get(0).getImgUrl());
             childOrder.setOperateTime(new Date());
             childOrder.setActivityReduceAmount(new BigDecimal("0"));
             childOrder.setCouponAmount(new BigDecimal("0"));
             childOrder.setOriginalTotalAmount(totalAmount);
             childOrder.setFeightFee(new BigDecimal("0"));
             childOrder.setWareId(item.getWareId());
             return  childOrder;
        }).collect(Collectors.toList());
         //对拆分的大订单进行遍历

        for (OrderInfo orderInfo : childOrders) {
            //TODO 将拆分好的子订单进行保存
            orderInfoService.save(orderInfo);
            Long id = orderInfo.getId();

            //保存子订单的明细
            List<OrderDetail> details = orderInfo.getOrderDetails().stream()
                    .map(item -> {
                        item.setOrderId(id); //回填子订单id
                        return item;
                    }).collect(Collectors.toList());
            //保存子订单明细
            orderDetailService.saveBatch(details);
        }
        //将父订单状态改为已拆分
        boolean update = orderInfoService.lambdaUpdate()
                .set(OrderInfo::getOrderStatus, OrderStatus.SPLIT.name())
                .set(OrderInfo::getProcessStatus, ProcessStatus.SPLIT.name())
                .eq(OrderInfo::getUserId, parentOrder.getUserId())
                .eq(OrderInfo::getId, parentOrder.getId())
                .update();
        //拿到拆分后订单的所有id集合
        List<Long> ids = childOrders.stream().map(item -> item.getId()).collect(Collectors.toList());

        log.info("订单拆分完成大订单",parentOrder.getId(),ids);

       //TODO 准备响应结果
        List<OrderSplitRespVo> collect = childOrders.stream().map(
                item -> {
                    OrderSplitRespVo respVo = new OrderSplitRespVo();
                    respVo.setOrderId(item.getId());
                    respVo.setUserId(item.getUserId());
                    respVo.setConsigee(item.getConsignee());
                    respVo.setConsigneeTel(item.getConsigneeTel());
                    respVo.setOrderComment(item.getOrderComment());
                    respVo.setOrderBody(item.getTradeBody());
                    respVo.setDriverAddress(item.getDeliveryAddress());
                    respVo.setPaymentWay(item.getPaymentWay());
                    respVo.setWareId(item.getWareId());

                    //订单明细、
                    List<OrderDetail> detailList = item.getOrderDetails();
                    List<OrderSplitRespVo.Sku> skuList = detailList.stream().map(obj -> {
                        OrderSplitRespVo.Sku sku = new OrderSplitRespVo.Sku();
                        sku.setSkuId(obj.getSkuId());
                        sku.setSkuNum(obj.getSkuNum());
                        sku.setSkuName(obj.getSkuName());
                        return sku;
                    }).collect(Collectors.toList());
                    respVo.setDetails(skuList);
                    return respVo;
                }
        ).collect(Collectors.toList());


        //保存子订单

//        orderInfoService.saveBatch(orderInfos);

        return collect;
    }


    /**
     * 保存订单明细
     * @param submitVo
     * @param orderInfo
     * @return
     */
    private List<OrderDetail> prepareOrderDetails(OrderSubmitVo submitVo, OrderInfo orderInfo) {
        List<OrderDetail> details = submitVo.getOrderDetailList()
                .stream()
                .map(item -> {
                    OrderDetail detail = new OrderDetail();
                    detail.setOrderId(orderInfo.getId());
                    detail.setUserId(orderInfo.getUserId());
                    detail.setSkuId(item.getSkuId());
                    detail.setSkuName(item.getSkuName());
                    detail.setImgUrl(item.getImgUrl());
                    detail.setOrderPrice(item.getOrderPrice());
                    detail.setSkuNum(item.getSkuNum());
                    detail.setCreateTime(new Date());
                    detail.setSplitTotalAmount(item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())));

                    detail.setSplitActivityAmount(new BigDecimal("0"));
                    detail.setSplitCouponAmount(new BigDecimal("0"));
                    return detail;
                })
                .collect(Collectors.toList());

     return  details;
    }

    /**
     * 封装订单基本信息
     * @param submitVo
     * @param tradeNo
     * @return
     */
    private OrderInfo  prepareOrderInfo(OrderSubmitVo submitVo, String tradeNo) {
        OrderInfo  info =new OrderInfo();
        info.setConsignee(submitVo.getConsignee());  //收货人
        info.setConsigneeTel(submitVo.getConsigneeTel()); //收货人手机号
        info.setDeliveryAddress(submitVo.getDeliveryAddress());//收货地址
        info.setOrderComment(submitVo.getOrderComment());

        //TODO 订单总额  =原价-优惠
        BigDecimal totalAmount = submitVo.getOrderDetailList()
                .stream()
                .map(item ->
                        item.getOrderPrice().multiply(new BigDecimal(item.getSkuNum())))
                .reduce((o1, o2) -> o1.add(o2))
                .get();
        info.setTotalAmount(totalAmount);

        //订单状态
        info.setOrderStatus(OrderStatus.UNPAID.name());
        //用户id
        Long userId = UserAuthUtil.getUserId();
        info.setUserId(userId);
        //支付方式
       info.setPaymentWay(PaymentWay.ONLINE.name());
       //对外流水号
        info.setOutTradeNo(tradeNo);
        //交易体
        String skuName = submitVo.getOrderDetailList().get(0).getSkuName();
        info.setTradeBody(skuName);
        //创建时间
        info.setCreateTime(new Date());
        //有限时间 30分钟
        Date date =new Date(System.currentTimeMillis()+30*60*1000);
        info.setExpireTime(date);
        //处理状态
        info.setProcessStatus(ProcessStatus.UNPAID.name());
        info.setTrackingNo("");
        info.setParentOrderId(null);

        String imgUrl = submitVo.getOrderDetailList().get(0).getImgUrl();
        info.setImgUrl(imgUrl);
        info.setProvinceId(0L);
        info.setOperateTime(new Date()); //操作时间
        info.setActivityReduceAmount(new BigDecimal("0"));
        info.setCouponAmount(new BigDecimal("0"));

        //原价金额
        info.setOriginalTotalAmount(totalAmount);
        info.setFeightFee(new BigDecimal("0"));

        return  info;
    }
}
